package com.zhaungjie.base.utils.LambdaStructureParser;


import com.zhaungjie.base.utils.StringUtils;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * lambda结构分析器
 *
 * @author zhuangjie
 * @date 2024/02/16
 */
public class LambdaStructureParser {

    public static final String GET = "get";

    public static final String IS = "is";

    private static final Map<Class<?>, SerializedLambda> CLASS_LAMBDA_CACHE = new ConcurrentHashMap<>();




    @FunctionalInterface
    public interface A1Function<T, A1> extends Serializable {

        // 主要的函数式接口方法，接受可变参数
        void apply(T target, A1 arg1);

    }

    @FunctionalInterface
    public interface A2Function<T, A1, A2> extends Serializable {

        // 主要的函数式接口方法，接受可变参数
        void apply(T target, A1 arg1, A2 arg2);

    }

    @FunctionalInterface
    public interface A3Function<T1, A1, A2, A3> extends Serializable {

        // 主要的函数式接口方法，接受可变参数
        void apply(T1 target, A1 arg1, A2 arg2, A3 arg3);

    }

    @FunctionalInterface
    public interface A4Function<T1, A1, A2, A3, A4> extends Serializable {
        // 主要的函数式接口方法，接受可变参数
        void apply(T1 target, A1 arg1, A2 arg2, A3 arg3, A4 arg4);
    }

    @FunctionalInterface
    public interface A5Function<T1, A1, A2, A3, A4, A5> extends Serializable {
        // 主要的函数式接口方法，接受可变参数
        void apply(T1 target, A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5);
    }

    @FunctionalInterface
    public interface NANRFunction<T> extends Serializable {
        void get(T that);
    }

    @FunctionalInterface
    public interface NAHRFunction<T, R> extends Serializable {
        R get(T that);
    }

    public static <T extends Serializable> String convertToFieldNameHandler(T fn) {
        SerializedLambda lambda = getSerializedLambda(fn);
        String methodName = lambda.getImplMethodName();
        if (methodName.startsWith(GET)) {
            methodName = methodName.substring(3);
        } else if (methodName.startsWith(IS)) {
            methodName = methodName.substring(2);
        } else {
            throw new IllegalArgumentException("无效的getter方法：" + methodName);
        }
        return StringUtils.firstToLowerCase(methodName);
    }
    /**
     * 转换方法引用为属性名
     *
     * @param fn
     * @param <T>
     * @return
     */
    public static <T> String convertToFieldName(NANRFunction<T> fn) {
        return convertToFieldNameHandler(fn);
    }

    /**
     * 转换方法引用为属性名
     *
     * @param fn
     * @param <T>
     * @return
     */
    public static <T> String convertToFieldName(NAHRFunction<T, ?> fn) {
        return convertToFieldNameHandler(fn);
    }


    public static<T,A1,A2,A3,A4,A5> Method convertToMethod(A5Function<T,A1,A2,A3,A4,A5> fun) {
        return convertToMethodHandler(fun);
    }
    public static<T,A1,A2,A3,A4> Method convertToMethod(A4Function<T,A1,A2,A3,A4> fun) {
        return convertToMethodHandler(fun);
    }
    public static<T,A1,A2,A3> Method convertToMethod(A3Function<T,A1,A2,A3> fun) {
        return convertToMethodHandler(fun);
    }
    public static<T,A1,A2> Method convertToMethod(A2Function<T,A1,A2> fun) {
        return convertToMethodHandler(fun);
    }
    public static<T,A1> Method convertToMethod(A1Function<T,A1> fun) {
        return convertToMethodHandler(fun);
    }
    public static<T> Method convertToMethod(NANRFunction<T> fun) {
        return convertToMethodHandler(fun);
    }


    /**
     * 解析方法param类
     *
     * @param serializedMethodParamStr 序列化方法param-str
     * @return {@link List}<{@link String}>
     */
    public static List<String> parseMethodParamClass(String serializedMethodParamStr) {
        final String regex = "([^();L]+);";
        final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
        final Matcher matcher = pattern.matcher(serializedMethodParamStr);
        List<String> list = new ArrayList<>();
        while (matcher.find()) {
            for (int i = 1; i <= matcher.groupCount(); i++) {
                list.add( matcher.group(i).replaceAll("/","."));
            }
        }
        return list;
    }

    public static<T extends Serializable> Method convertToMethodHandler(T fun) {
        SerializedLambda serializedLambda = getSerializedLambda(fun);
        serializedLambda.getInstantiatedMethodType();
        String instantiatedMethodType = serializedLambda.getInstantiatedMethodType();
        List<String> paramClassList = parseMethodParamClass(instantiatedMethodType);
        try {
            Class<?> targetClass = Class.forName(serializedLambda.getImplClass().replaceAll("/", "."));
            Method[] methods = targetClass.getMethods();
            String targetMethodName = serializedLambda.getImplMethodName();

            for (Method method : methods) {
                // 看方法名是否相同
                if (!method.getName().equals(targetMethodName)) continue;
                // 看参数类型是否相同
                for (int i = 0; i < method.getParameterTypes().length; i++) {
                    if (!method.getParameterTypes()[i].getName().equals(paramClassList.get(i))) break;
                }
                // 上述条件满足
                return method;
            }
        } catch (Exception e) {}
        return null;
    }


    private static Method getPrivateMethodFromClassHierarchy(Class<?> clazz, String methodName) {
        Method method = null;

        while (clazz != null) {
            try {
                method = clazz.getDeclaredMethod(methodName);
                break; // 方法找到，跳出循环
            } catch (NoSuchMethodException e) {
                clazz = clazz.getSuperclass(); // 继续向父类寻找
            }
        }

        return method;
    }

    /**
     * 转换为方法名称
     *
     * @param fn fn
     * @return {@link String}
     */
    public static <T> String convertToMethodName(NANRFunction<T> fn) {
        SerializedLambda lambda = getSerializedLambda(fn);
        String methodName = lambda.getImplMethodName();
        return StringUtils.firstToLowerCase(methodName);
    }



    public static SerializedLambda getSerializedLambda(Serializable fn) {
        SerializedLambda lambda = CLASS_LAMBDA_CACHE.get(fn.getClass());
        // 先检查缓存中是否存在
        if (lambda == null) {
            try {
                // 提取SerializedLambda并缓存
                Method method = fn.getClass().getDeclaredMethod("writeReplace");
                method.setAccessible(Boolean.TRUE);
                lambda = (SerializedLambda) method.invoke(fn);
                CLASS_LAMBDA_CACHE.put(fn.getClass(), lambda);
            } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return lambda;
    }
}